<?xml version="1.0" encoding="utf-8"?>

<!--
***********************************************************************************************
CLRTest.Jit.targets

WARNING:  DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have
          created a backup copy.  Incorrect changes to this file will make it
          impossible to load or build your projects from the command-line or the IDE.

This file contains the logic for generating command scripts for special GC tests.

WARNING:   When setting properties based on their current state (for example:
           <Foo Condition="'$(Foo)'==''>Bar</Foo>).  Be very careful.  Another script generation
           target might be trying to do the same thing.  It's better to avoid this by instead setting a new property.
           
           Additionally, be careful with itemgroups.  Include will propagate outside of the target too!

***********************************************************************************************
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <PropertyGroup>
    <BashScriptSnippetGen>$(BashScriptSnippetGen);GetIlasmRoundTripBashScript;GetDisasmCheckBashScript</BashScriptSnippetGen>
    <BatchScriptSnippetGen>$(BatchScriptSnippetGen);GetIlasmRoundTripBatchScript;GetDisasmCheckBatchScript</BatchScriptSnippetGen>
  </PropertyGroup>

  <!--
  ***********************************************************************************************
  ildasm / ilasm round trip testing

  Note: https://github.com/dotnet/runtime/issues/4873 describes an issue with ildasm that requires
  us to use the "/raweh" argument.
  ***********************************************************************************************
  -->

  <PropertyGroup>
    <_IlasmSwitches Condition="'$(DebugType)' == 'Full' and '$(Optimize)' != 'True'">$(_IlasmSwitches) -DEBUG</_IlasmSwitches>
    <_IlasmSwitches Condition="'$(DebugType)' == 'Impl' and '$(Optimize)' != 'True'">$(_IlasmSwitches) -DEBUG=IMPL</_IlasmSwitches>
    <_IlasmSwitches Condition="'$(DebugType)' == 'PdbOnly' and '$(Optimize)' != 'True'">$(_IlasmSwitches) -DEBUG</_IlasmSwitches>
  </PropertyGroup>

  <Target Name="GetIlasmRoundTripBashScript"
          Returns="$(IlasmRoundTripBashScript);$(BashIlrtTestLaunchCmds)">
    <PropertyGroup>
      <DisassemblyName>IL-RT/$(AssemblyName).il</DisassemblyName>
      <TargetAssemblyName>IL-RT/$(AssemblyName).dll</TargetAssemblyName>

      <IlasmRoundTripBashScript Condition="'$(CLRTestKind)' == 'RunOnly'"><![CDATA[
# IlasmRoundTrip Script
# For CLRTestKind==RunOnly, we don't do any ilasm round-trip testing. We also need to disable
# ilasm round-trip testing for the project we call, as there might be multiple RunOnly tests
# concurrently invoking the same project, which can lead to race conditions on the ilasm/ildasm commands.
export RunningIlasmRoundTrip=
]]>
      </IlasmRoundTripBashScript>

      <IlasmRoundTripBashScript Condition="'$(CLRTestKind)' == 'BuildAndRun'"><![CDATA[
# IlasmRoundTrip Script
# Disable Ilasm round-tripping for Linker tests.
# Todo: Ilasm round-trip on linked binaries.

if [ -z "$DoLink" -a ! -z "$RunningIlasmRoundTrip" ];
then
  python3 $(AssemblyName)_ilasmroundtrip.py
  ERRORLEVEL=$?
  if [ $ERRORLEVEL -ne 0 ]
  then
    echo ILASM ROUND-TRIP - FAILED $ERRORLEVEL
    exit 1
  fi
fi
]]>
      </IlasmRoundTripBashScript>
    </PropertyGroup>
  </Target>

  <Target Name="GetIlasmRoundTripBatchScript"
          Returns="$(IlasmRoundTripBatchScript);$(BatchIlrtTestLaunchCmds)">
    <PropertyGroup>
      <DisassemblyName>IL-RT\$(AssemblyName).il</DisassemblyName>
      <TargetAssemblyName>IL-RT\$(AssemblyName).dll</TargetAssemblyName>

      <IlasmRoundTripBatchScript Condition="'$(CLRTestKind)' == 'RunOnly'"><![CDATA[
REM IlasmRoundTrip Script
REM For CLRTestKind==RunOnly, we don't do any ilasm round-trip testing. We also need to disable
REM ilasm round-trip testing for the project we call, as there might be multiple RunOnly tests
REM concurrently invoking the same project, which can lead to race conditions on the ilasm/ildasm commands.
set RunningIlasmRoundTrip=
]]>
      </IlasmRoundTripBatchScript>

      <IlasmRoundTripBatchScript Condition="'$(CLRTestKind)' == 'BuildAndRun'"><![CDATA[
REM IlasmRoundTrip Script
REM Disable Ilasm round-tripping for Linker tests.
REM Todo: Ilasm round-trip on linked binaries.

IF NOT DEFINED DoLink (
  IF DEFINED RunningIlasmRoundTrip (
    python $(AssemblyName)_ilasmroundtrip.py
    IF NOT "!ERRORLEVEL!"=="0" (
      ECHO ILASM ROUND-TRIP - FAILED !ERRORLEVEL!
      Exit /b 1
    )
  )
)
]]>
      </IlasmRoundTripBatchScript>
    </PropertyGroup>
  </Target>

  <Target Name="GetDisasmCheckData">
    <ItemGroup>
      <DisasmCheckFiles Include="%(Compile.Identity)" Condition="'%(Compile.HasDisasmCheck)' == 'true'" />
    </ItemGroup>
    <PropertyGroup>
      <HasDisasmCheck>false</HasDisasmCheck>
      <HasDisasmCheck Condition="@(DisasmCheckFiles->Count()) &gt; 0 And '$(CLRTestKind)' == 'BuildAndRun'">true</HasDisasmCheck>

      <GCStressIncompatible Condition="'$(HasDisasmCheck)' == 'true'">true</GCStressIncompatible>
      <HeapVerifyIncompatible Condition="'$(HasDisasmCheck)' == 'true'">true</HeapVerifyIncompatible>
    </PropertyGroup>
  </Target>

  <!--
  The source code file(s) (specified by HasDisasmCheck in the test's project file) contain
  the patterns used by FileCheck.  They need to be copied to the output directory for the
  test to be self-contained.
  -->
  <Target Name="PropagateHasDisasmCheckToCopy"
          BeforeTargets="AssignTargetPaths">
    <ItemGroup>
      <Compile Update="@(Compile)"
               Condition="'%(Compile.HasDisasmCheck)' == 'true'"
               CopyToOutputDirectory="PreserveNewest" />
    </ItemGroup>
  </Target>

  <Target Name="GetDisasmCheckBashScript"
          DependsOnTargets="GetDisasmCheckData">
    <PropertyGroup>
      <HasBashDisasmCheck>false</HasBashDisasmCheck>
      <HasBashDisasmCheck Condition="'$(HasDisasmCheck)' == 'true' and '$(RuntimeFlavor)' == 'coreclr' and ('$(TargetOS)' == 'linux' or '$(TargetOS)' == 'osx') and ('$(TargetArchitecture)' == 'x64' or '$(TargetArchitecture)' == 'arm64')">true</HasBashDisasmCheck>

      <BashDisasmOutputFile Condition="'$(HasBashDisasmCheck)' == 'true'">$(scriptPath)__jit_disasm.out</BashDisasmOutputFile>
      <BashDisasmListOutputFile Condition="'$(HasBashDisasmCheck)' == 'true'">$(scriptPath)__jit_disasm_list.out</BashDisasmListOutputFile>

      <CLRTestBashPreCommands Condition="'$(HasBashDisasmCheck)' == 'true'"><![CDATA[
$(CLRTestBashPreCommands)
if [[ ( -z "$DOTNET_JitStress" ) && ( -z "$DOTNET_JitStressRegs" ) && ( -z "$DOTNET_TailcallStress" ) && ( "$DOTNET_TieredPGO" != "1" ) && ( -z "$RunCrossGen2" ) && ( -z "$DOTNET_JitForceControlFlowGuard" ) ]]; then
  @(DisasmCheckFiles -> '  dotnet $CORE_ROOT/SuperFileCheck/SuperFileCheck.dll --csharp-list-method-names "%(Identity)" --allow-unused-prefixes --check-prefixes=CHECK,$(TargetArchitecture.ToUpperInvariant()),$(TargetArchitecture.ToUpperInvariant())-$(TargetOS.ToUpperInvariant()) > "$(BashDisasmListOutputFile)"
    ERRORLEVEL=$?
    export DOTNET_JitDisasm=`cat $(BashDisasmListOutputFile)`
    export DOTNET_JitDisasmTesting=1
    export DOTNET_JitDisasmDiffable=1
    export DOTNET_JitStdOutFile=$(BashDisasmOutputFile)
    rm -f $(BashDisasmOutputFile)
    if [[ $ERRORLEVEL -ne 0 ]]
    then
      echo EXECUTION OF FILECHECK - FAILED $ERRORLEVEL
      exit 1
    fi', '%0a')
fi
]]>
      </CLRTestBashPreCommands>

      <CLRTestBashPostCommands Condition="'$(HasBashDisasmCheck)' == 'true'"><![CDATA[
$(CLRTestBashPostCommands)
if [[ -n $DOTNET_JitDisasm ]]; then
  @(DisasmCheckFiles -> '  dotnet $CORE_ROOT/SuperFileCheck/SuperFileCheck.dll --csharp "%(Identity)" --allow-unused-prefixes --check-prefixes=CHECK,$(TargetArchitecture.ToUpperInvariant()),$(TargetArchitecture.ToUpperInvariant())-$(TargetOS.ToUpperInvariant()) --dump-input-context 40 --input-file "$(BashDisasmOutputFile)"
    ERRORLEVEL=$?
    if [[ $ERRORLEVEL -ne 0 ]]
    then
      echo EXECUTION OF FILECHECK - FAILED $ERRORLEVEL
      exit 1
    fi', '%0a')
fi
]]>
      </CLRTestBashPostCommands>
    </PropertyGroup>
  </Target>

  <Target Name="GetDisasmCheckBatchScript"
          DependsOnTargets="GetDisasmCheckData">
    <PropertyGroup>
      <HasBatchDisasmCheck>false</HasBatchDisasmCheck>
      <HasBatchDisasmCheck Condition="'$(HasDisasmCheck)' == 'true' and '$(RuntimeFlavor)' == 'coreclr'">true</HasBatchDisasmCheck>

      <BatchDisasmOutputFile Condition="'$(HasBatchDisasmCheck)' == 'true'">$(scriptPath)__jit_disasm.out</BatchDisasmOutputFile>
      <BatchDisasmListOutputFile Condition="'$(HasBatchDisasmCheck)' == 'true'">$(scriptPath)__jit_disasm_list.out</BatchDisasmListOutputFile>

      <CLRTestBatchPreCommands Condition="'$(HasBatchDisasmCheck)' == 'true'">
<![CDATA[
$(CLRTestBatchPreCommands)
IF "%DOTNET_JitStress%"=="" IF "%DOTNET_JitStressRegs%"=="" IF "%DOTNET_TailcallStress%"=="" IF NOT "%DOTNET_TieredPGO%" == "1" IF NOT "%RunCrossGen2%" == "1" IF "%DOTNET_JitForceControlFlowGuard%"=="" (
  @(DisasmCheckFiles -> '  dotnet %CORE_ROOT%\SuperFileCheck\SuperFileCheck.dll --csharp-list-method-names "%(Identity)" --check-prefixes=CHECK,$(TargetArchitecture.ToUpperInvariant()),$(TargetArchitecture.ToUpperInvariant())-$(TargetOS.ToUpperInvariant()) > "$(BatchDisasmListOutputFile)"
    IF NOT "!ERRORLEVEL!" == "0" (
      ECHO EXECUTION OF FILECHECK LISTING METHOD NAMES - FAILED !ERRORLEVEL!
      Exit /b 1
    )', '%0d%0a')
    for /F "delims=" %%g in ($(BatchDisasmListOutputFile)) do set DOTNET_JitDisasm=%%g
    set DOTNET_JitDisasmTesting=1
    set DOTNET_JitDisasmDiffable=1
    set DOTNET_JitStdOutFile=$(BatchDisasmOutputFile)
    del $(BatchDisasmOutputFile) >nul 2>&1
)
]]>
      </CLRTestBatchPreCommands>

      <CLRTestBatchPostCommands Condition="'$(HasBatchDisasmCheck)' == 'true'"><![CDATA[
$(CLRTestBatchPostCommands)
IF NOT "%DOTNET_JitDisasm%" == "" (
  @(DisasmCheckFiles -> '  dotnet %CORE_ROOT%\SuperFileCheck\SuperFileCheck.dll --csharp "%(Identity)" --allow-unused-prefixes --check-prefixes=CHECK,$(TargetArchitecture.ToUpperInvariant()),$(TargetArchitecture.ToUpperInvariant())-$(TargetOS.ToUpperInvariant()) --dump-input-context 40 --input-file "$(BatchDisasmOutputFile)"
    IF NOT "!ERRORLEVEL!" == "0" (
      ECHO EXECUTION OF FILECHECK - FAILED !ERRORLEVEL!
      Exit /b 1
    )', '%0d%0a')
)
]]>
      </CLRTestBatchPostCommands>
    </PropertyGroup>
  </Target>

  <!--
  ***********************************************************************************************
  SuperPMI collection of CoreCLR tests
  ***********************************************************************************************
  
  You shouldn't have to escape characters in a CDATA block, but it appears that you do actually need
  to escape semicolons with %3B here.
  -->

  <Target Name="GetSuperPMICollectionBashScript"
          Returns="$(SuperPMICollectionBashScript)">
    <PropertyGroup>
      <SuperPMICollectionBashScript><![CDATA[
# SuperPMI collection
if [ ! -z $spmi_enable_collection ]%3B
then
  # spmi_collect_dir needs to be set before this script is run, if SuperPMI collection is enabled.
  if [ -z $spmi_collect_dir ]%3B
  then
    echo "ERROR - spmi_collect_dir not defined"
    exit 1
  fi
  mkdir -p $spmi_collect_dir
  export spmi_file_extension=so
  if [[ "$OSTYPE" == "darwin"* ]]%3B then
    export spmi_file_extension=dylib
  fi
  export spmi_jitlib=$CORE_ROOT/libclrjit.$spmi_file_extension
  export SuperPMIShimLogPath=$spmi_collect_dir
  export SuperPMIShimPath=$spmi_jitlib
  export DOTNET_EnableExtraSuperPmiQueries=1
  export DOTNET_JitPath=$CORE_ROOT/libsuperpmi-shim-collector.$spmi_file_extension
fi
]]>
      </SuperPMICollectionBashScript>
    </PropertyGroup>
  </Target>

  <Target Name="GetSuperPMICollectionBatchScript"
          Returns="$(SuperPMICollectionBatchScript)">
    <PropertyGroup>
      <SuperPMICollectionBatchScript><![CDATA[
REM SuperPMI collection
if not defined spmi_enable_collection goto :skip_spmi_enable_collection
REM spmi_collect_dir needs to be set before this script is run, if SuperPMI collection is enabled.
if not defined spmi_collect_dir echo ERROR: spmi_collect_dir not defined&goto :skip_spmi_enable_collection
if not exist %spmi_collect_dir% mkdir %spmi_collect_dir%
set spmi_jitlib=%CORE_ROOT%\clrjit.dll
set SuperPMIShimLogPath=%spmi_collect_dir%
set SuperPMIShimPath=%spmi_jitlib%
set DOTNET_EnableExtraSuperPmiQueries=1
set DOTNET_JitPath=%CORE_ROOT%\superpmi-shim-collector.dll
:skip_spmi_enable_collection
]]>
      </SuperPMICollectionBatchScript>
    </PropertyGroup>
  </Target>

  <!--
  ***********************************************************************************************
  IlasmRoundTrip for merged tests
  ***********************************************************************************************
  -->

  <Target Name="IlasmRoundTripCommand" AfterTargets="AfterBuild" Condition="'$(IlasmRoundTripIncompatible)' != 'true'">
    <PropertyGroup>
      <InputAssemblyName>$(AssemblyName).dll</InputAssemblyName>
      <DisassemblyName>IL-RT/$(AssemblyName).il</DisassemblyName>
      <TargetAssemblyName>IL-RT/$(AssemblyName).dll</TargetAssemblyName>
      <_IlasmRoundTripScriptText><![CDATA[
import os
import shutil
import subprocess
import sys
import glob

def is_managed_assembly(file):
    proc = subprocess.Popen([f'{os.environ["CORE_ROOT"]}/corerun', f'{os.environ["CORE_ROOT"]}/AssemblyChecker/AssemblyChecker.dll', file])

    try:
        proc.communicate()
        return proc.returncode == 0
    except:
        proc.kill()
        return False

def is_managed_debug_assembly(file):
    proc = subprocess.Popen([f'{os.environ["CORE_ROOT"]}/corerun', f'{os.environ["CORE_ROOT"]}/AssemblyChecker/AssemblyChecker.dll', "--is-debug", file])

    try:
        proc.communicate()
        return proc.returncode == 0
    except:
        proc.kill()
        return False

def is_managed_exe_assembly(file):
    proc = subprocess.Popen([f'{os.environ["CORE_ROOT"]}/corerun', f'{os.environ["CORE_ROOT"]}/AssemblyChecker/AssemblyChecker.dll', "--is-exe", file])

    try:
        proc.communicate()
        return proc.returncode == 0
    except:
        proc.kill()
        return False

print("")
print("ILASM RoundTrips")

if not os.path.exists("IL-RT"):
  os.mkdir("IL-RT")

for inputAssemblyName in glob.glob("*.dll"):
  if is_managed_assembly(inputAssemblyName):
      print("ILASM RoundTrip for " + inputAssemblyName)

      ilasmSwitches = ""

      if not is_managed_exe_assembly(inputAssemblyName):
        ilasmSwitches = ilasmSwitches + " -DLL"

      if is_managed_debug_assembly(inputAssemblyName):
        ilasmSwitches = ilasmSwitches + " -DEBUG"

      disassemblyName = os.path.join("IL-RT", os.path.splitext(os.path.basename(inputAssemblyName))[0] + ".il")
      targetAssemblyName = os.path.join("IL-RT", os.path.basename(inputAssemblyName))

      shutil.copyfile(inputAssemblyName, targetAssemblyName)

      ildasm_args = f'{os.environ["CORE_ROOT"]}/ildasm -raweh -unicode -out={disassemblyName} {targetAssemblyName}'
      print(ildasm_args)
      proc = subprocess.Popen(ildasm_args, shell=True)

      try:
          proc.communicate()
      except:
          proc.kill()
          sys.exit(1)

      ilasm_args = f'{os.environ["CORE_ROOT"]}/ilasm -output={inputAssemblyName} {ilasmSwitches} {disassemblyName}'
      print(ilasm_args)
      proc = subprocess.Popen(ilasm_args, shell=True)

      try:
          proc.communicate()
      except:
          proc.kill()
          sys.exit(1)

      print("")

print("")

]]></_IlasmRoundTripScriptText>
    </PropertyGroup>
    <WriteLinesToFile
      File="$(OutputPath)$(AssemblyName)_ilasmroundtrip.py"
      Lines="$(_IlasmRoundTripScriptText)"
      Overwrite="true" />
  </Target>

  <!--
  ***********************************************************************************************
  GCStress settings
  ***********************************************************************************************
  -->

  <ItemGroup>
    <CLRTestEnvironmentVariable Condition="'$(RunWithGcStress)' != ''"
      Include="DOTNET_GCStress" Value="$(RunWithGcStress)" />
  </ItemGroup>

</Project> 
